//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 1995-2000, The Hypersonic SQL Group.
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Hypersonic SQL Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * on behalf of the Hypersonic SQL Group.
 *
 *
 * For work added by the HSQL Development Group:
 *
 * Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reflection;
using System.IO;
using EffiProzDB.Lib;
using EffiProzDB.ScriptIO;
using EffiProzDB.Persist;


namespace EffiProzDB
{

    /**
     * Provides SQL Interpreter services relative to a Session and
     * its Database.
     *
     * The core functionality of this class was inherited from Hypersonic and
     * extensively rewritten and extended _in successive versions of HSQLDB.
     *
     * @author Thomas Mueller (Hypersonic SQL Group)
     * @version 1.8.0
     * @since 1.7.2
     */

    // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) - various corrections
    // fredt@users 20020430 - patch 549741 by velichko - ALTER TABLE RENAME
    // fredt@users 20020405 - patch 1.7.0 - other ALTER TABLE statements
    // tony_lai@users 20020820 - patch 595099 - use user-defined PK name
    // tony_lai@users 20020820 - patch 595156 - violation of constraint name
    // fredt@users 20020912 - patch 1.7.1 by fredt - log alter statements
    // kloska@users 20021030 - patch 1.7.2 - ON UPDATE CASCADE | SET NULL | SET DEFAULT
    // kloska@users 20021112 - patch 1.7.2 - ON DELETE SET NULL | SET DEFAULT
    // boucherb@users 20020310 - disable ALTER TABLE DDL on VIEWs (avoid NPE)
    // fredt@users 20030314 - patch 1.7.2 by gilead@users - drop table if exists syntax
    // boucherb@users 20030425 - DDL methods are moved to DatabaseCommandInterpreter.java
    // boucherb@users 20030425 - refactoring DDL methods into smaller units
    // fredt@users 20030609 - support for ALTER COLUMN SET/DROP DEFAULT / RENAME TO
    // wondersonic@users 20031205 - IF EXISTS support for DROP INDEX
    // fredt@users 20031224 - support for CREATE SEQUENCE ...
    // fredt@users 20041209 - patch by tytar@users to set default table type
    public class DatabaseCommandInterpreter
    {

        private Tokenizer tokenizer = new Tokenizer();
        private Database database;
        private Session session;

        /**
         * Constructs a new DatabaseCommandInterpreter for the given Session
         *
         * @param s session
         */
        public DatabaseCommandInterpreter(Session s)
        {
            session = s;
            database = s.getDatabase();
        }

        /**
         * Executes the SQL String. This method is always called from a block
         * synchronized on the database object.
         *
         * @param sql query
         * @return the result of executing the given SQL String
         */
        public Result execute(string sql)
        {

            Result result;
            string token;
            int cmd;

          //  System.GC.Collect();

            result = null;
            cmd = Token.UNKNOWNTOKEN;

            try
            {
                tokenizer.reset(sql);

                while (true)
                {
                    tokenizer.setPartMarker();
                    session.setScripting(false);

                    token = tokenizer.getSimpleToken();

                    if (token.Length == 0)
                    {
                        session.endSchemaDefinition();

                        break;
                    }

                    cmd = Token.get(token);

                    if (cmd == Token.SEMICOLON)
                    {
                        session.endSchemaDefinition();

                        continue;
                    }

                    result = executePart(cmd, token);

                    if (result.isError())
                    {
                        session.endSchemaDefinition();

                        break;
                    }

                    if (session.getScripting())
                    {
                        database.logger.writeToLog(session,
                                                   tokenizer.getLastPart());
                    }
                }
            }
            catch (Exception t)
            {
                try
                {
                    if (session.isSchemaDefintion())
                    {
                        HsqlNameManager.HsqlName schemaName = session.getSchemaHsqlName(null);

                        database.schemaManager.dropSchema(schemaName.name, true);
                        database.logger.writeToLog(session,
                                                   Token.T_DROP + ' '
                                                   + Token.T_SCHEMA + ' '
                                                   + schemaName.statementName
                                                   + ' ' + Token.T_CASCADE);
                        session.endSchemaDefinition();
                    }
                }
                catch (HsqlException) { }

                result = new Result(t, tokenizer.getLastPart());
            }

            return result == null ? Session.emptyUpdateCount
                                  : result;
        }

        private Result executePart(int cmd, string token)
        {

            Result result = Session.emptyUpdateCount;
            int brackets = 0;

            if (session.isSchemaDefintion())
            {
                switch (cmd)
                {

                    case Token.CREATE:
                    case Token.GRANT:
                        break;

                    default:
                        throw Trace.error(Trace.INVALID_IDENTIFIER,
                                          Trace.IN_SCHEMA_DEFINITION,
                                          new Object[] { token });
                }
            }

            switch (cmd)
            {

                case Token.OPENBRACKET:
                    {
                        Parser parser = new Parser(session, database, tokenizer);

                        brackets = parser.parseOpenBracketsSelect() + 1;
                    }
                    goto case Token.SELECT;
                case Token.SELECT:
                    {
                        Parser parser = new Parser(session, database, tokenizer);
                        CompiledStatement cStatement =
                            parser.compileSelectStatement(brackets);

                        if (cStatement.parameters.Length != 0)
                        {
                            Trace.doAssert(
                                false,
                                Trace.getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
                        }

                        result = session.sqlExecuteCompiledNoPreChecks(cStatement,
                                null);

                        break;
                    }
                case Token.INSERT:
                    {
                        Parser parser = new Parser(session, database, tokenizer);
                        CompiledStatement cStatement = parser.compileInsertStatement();

                        if (cStatement.parameters.Length != 0)
                        {
                            Trace.doAssert(
                                false,
                                Trace.getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
                        }

                        result = session.sqlExecuteCompiledNoPreChecks(cStatement,
                                null);

                        break;
                    }
                case Token.UPDATE:
                    {
                        Parser parser = new Parser(session, database, tokenizer);
                        CompiledStatement cStatement = parser.compileUpdateStatement();

                        if (cStatement.parameters.Length != 0)
                        {
                            Trace.doAssert(
                                false,
                                Trace.getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
                        }

                        result = session.sqlExecuteCompiledNoPreChecks(cStatement,
                                null);

                        break;
                    }
                case Token.DELETE:
                    {
                        Parser parser = new Parser(session, database, tokenizer);
                        CompiledStatement cStatement = parser.compileDeleteStatement();

                        if (cStatement.parameters.Length != 0)
                        {
                            Trace.doAssert(
                                false,
                                Trace.getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
                        }

                        result = session.sqlExecuteCompiledNoPreChecks(cStatement,
                                null);

                        break;
                    }
                case Token.CALL:
                    {
                        Parser parser = new Parser(session, database, tokenizer);
                        CompiledStatement cStatement = parser.compileCallStatement();

                        if (cStatement.parameters.Length != 0)
                        {
                            Trace.doAssert(
                                false,
                                Trace.getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
                        }

                        result = session.sqlExecuteCompiledNoPreChecks(cStatement,
                                null);

                        break;
                    }
                case Token.SET:
                    processSet();
                    break;

                case Token.COMMIT:
                    processCommit();
                    break;

                case Token.ROLLBACK:
                    processRollback();
                    break;

                case Token.SAVEPOINT:
                    processSavepoint();
                    break;

                case Token.RELEASE:
                    processReleaseSavepoint();
                    break;

                case Token.CREATE:
                    processCreate();
                    database.setMetaDirty(false);
                    break;

                case Token.ALTER:
                    processAlter();
                    database.setMetaDirty(true);
                    break;

                case Token.DROP:
                    processDrop();
                    database.setMetaDirty(true);
                    break;

                case Token.GRANT:
                    processGrantOrRevoke(true);
                    database.setMetaDirty(false);
                    break;

                case Token.REVOKE:
                    processGrantOrRevoke(false);
                    database.setMetaDirty(true);
                    break;

                case Token.CONNECT:
                    processConnect();
                    database.setMetaDirty(false);
                    session.setScripting(false);
                    break;

                case Token.DISCONNECT:
                    processDisconnect();
                    session.setScripting(true);
                    break;

                case Token.SCRIPT:
                    result = processScript();
                    break;

                case Token.SHUTDOWN:
                    processShutdown();
                    break;

                case Token.CHECKPOINT:
                    processCheckpoint();
                    break;

                case Token.EXPLAIN:
                    result = processExplainPlan();
                    break;

                default:
                    throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
            }

            return result;
        }

        /**
         * Responsible for parsing and executing the SCRIPT SQL statement
         *
         * @return either an empty result or one _in which each row is a DDL or DML
         * @ 
         * @ 
         */
        private Result processScript()
        {

            string token = tokenizer.getString();
            ScriptWriterText dsw = null;

            session.checkAdmin();

            try
            {
                if (tokenizer.wasValue())
                {
                    if (tokenizer.getType() != Types.VARCHAR)
                    {
                        throw Trace.error(Trace.INVALID_IDENTIFIER);
                    }

                    dsw = new ScriptWriterText(database, token, true, true, true);

                    dsw.writeAll();

                    return new Result(ResultConstants.UPDATECOUNT);
                }
                else
                {
                    tokenizer.back();

                    return DatabaseScript.getScript(database, false);
                }
            }
            finally
            {
                if (dsw != null)
                {
                    dsw.close();
                }
            }
        }

        /**
         *  Responsible for handling CREATE ...
         *
         *  All CREATE command require an ADMIN user except: <p>
         *
         * <pre>
         * CREATE TEMP [MEMORY] TABLE
         * </pre>
         *
         * @throws  HsqlException
         */
        private void processCreate()
        {

            bool unique = false;
            int tableType;
            bool isTempTable = false;
            string token;

            session.checkAdmin();
            session.checkDDLWrite();
            session.setScripting(true);

            if (tokenizer.isGetThis(Token.T_GLOBAL))
            {
                tokenizer.getThis(Token.T_TEMPORARY);

                isTempTable = true;
            }
            else if (tokenizer.isGetThis(Token.T_TEMP))
            {
                isTempTable = true;
            }
            else if (tokenizer.isGetThis(Token.T_TEMPORARY))
            {
                isTempTable = true;
            }

            token = tokenizer.getSimpleToken();

            switch (Token.get(token))
            {

                // table
                case Token.MEMORY:
                    tokenizer.getThis(Token.T_TABLE);
                    tableType =  Table.MEMORY_TABLE ;

                    processCreateTable(tableType);
                    return;
                case Token.TABLE:
                    tableType = isTempTable ? Table.TEMP_TABLE
                                            : database.getDefaultTableType();

                    processCreateTable(tableType);

                    return;

                case Token.CACHED:
                    if (isTempTable)
                    {
                        throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                    }

                    tokenizer.getThis(Token.T_TABLE);
                    processCreateTable(Table.CACHED_TABLE);

                    return;

                case Token.TEXT:
                    if (isTempTable)
                    {
                        throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                    }

                    tokenizer.getThis(Token.T_TABLE);
                    processCreateTable(Table.TEXT_TABLE);

                    return;

                default:
                    if (isTempTable)
                    {
                        throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                    }
                    break;
            }

            switch (Token.get(token))
            {

                // other objects
                case Token.ALIAS:
                    processCreateAlias();
                    break;

                case Token.SEQUENCE:
                    processCreateSequence();
                    break;

                case Token.SCHEMA:
                    session.setScripting(false);
                    processCreateSchema();
                    break;

                case Token.TRIGGER:
                    processCreateTrigger();
                    break;

                case Token.USER:
                    processCreateUser();
                    break;

                case Token.ROLE:
                    database.getGranteeManager().addRole(getUserIdentifier());
                    break;

                case Token.VIEW:
                    processCreateView();
                    break;

                // index
                case Token.UNIQUE:
                    unique = true;

                    tokenizer.getThis(Token.T_INDEX);
                    processCreateIndex(unique);
                    break;
                //fall thru
                case Token.INDEX:
                    processCreateIndex(unique);
                    break;

                default:
                    {
                        throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                    }
            }
        }

        /**
         *  Process a bracketed column list as used _in the declaration of SQL
         *  CONSTRAINTS and return an array containing the indexes of the columns
         *  within the table.
         *
         * @param  t table that contains the columns
         * @return  column index map
         * @throws  HsqlException if a column is not found or is duplicate
         */
        private int[] processColumnList(Table t,
                                        bool acceptAscDesc)
        {

            HashMappedList<string, Expression> list = Parser.processColumnList(tokenizer,
                acceptAscDesc);
            int size = list.Count;
            int[] col = new int[size];

            for (int i = 0; i < size; i++)
            {
                col[i] = t.getColumnNr((String)list.Keys[(i)]);
            }

            return col;
        }

        /**
         *  Responsible for handling the execution of CREATE TRIGGER SQL
         *  statements. <p>
         *
         *  typical sql is: CREATE TRIGGER tr1 AFTER INSERT ON tab1 CALL "pkg.cls"
         *
         * @ 
         */
        private void processCreateTrigger()
        {

            Table t;
            bool isForEach;
            bool isNowait;
            int queueSize;
            string triggerName;
            bool isQuoted;
            string sWhen;
            string sOper;
            string tableName;
            string token;
            string className;
            TriggerDef td;


            triggerName = tokenizer.getName();

            string schemaname = tokenizer.getLongNameFirst();

            database.schemaManager.checkTriggerExists(triggerName,
                    session.getSchemaNameForWrite(schemaname), false);

            isQuoted = tokenizer.wasQuotedIdentifier();
            isForEach = false;
            isNowait = false;
            queueSize = TriggerDef.getDefaultQueueSize();
            sWhen = tokenizer.getSimpleToken();
            sOper = tokenizer.getSimpleToken();

            tokenizer.getThis(Token.T_ON);

            tableName = tokenizer.getName();

            if (schemaname == null)
            {
                schemaname =
                    session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
            }
            else if (!schemaname.Equals(
                  session.getSchemaNameForWrite(tokenizer.getLongNameFirst())))
            {
                throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
            }

            t = database.schemaManager.getUserTable(session, tableName,
                    schemaname);

            if (t.isView())
            {
                throw Trace.error(Trace.NOT_A_TABLE);
            }

            session.setScripting(true);

            // "FOR EACH ROW" or "CALL"
            token = tokenizer.getSimpleToken();

            if (token.Equals(Token.T_FOR))
            {
                token = tokenizer.getSimpleToken();

                if (token.Equals(Token.T_EACH))
                {
                    token = tokenizer.getSimpleToken();

                    if (token.Equals(Token.T_ROW))
                    {
                        isForEach = true;

                        // should be 'NOWAIT' or 'QUEUE' or 'CALL'
                        token = tokenizer.getSimpleToken();
                    }
                    else
                    {
                        throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, token);
                    }
                }
                else
                {
                    throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, token);
                }
            }

            if (token.Equals(Token.T_NOWAIT))
            {
                isNowait = true;

                // should be 'CALL' or 'QUEUE'
                token = tokenizer.getSimpleToken();
            }

            if (token.Equals(Token.T_QUEUE))
            {
                queueSize = tokenizer.getInt();

                // should be 'CALL'
                token = tokenizer.getSimpleToken();
            }

            if (!token.Equals(Token.T_CALL))
            {
                throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, token);
            }

            className = tokenizer.getSimpleName();

            if (!tokenizer.wasQuotedIdentifier())
            {
                throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, className);
            }

            HsqlNameManager.HsqlName name = database.nameManager.newHsqlName(triggerName,
                isQuoted);

            td = new TriggerDef(name, sWhen, sOper, isForEach, t, className,
                                isNowait, queueSize);

            t.addTrigger(td);

            if (td.isValid())
            {
                try
                {

                    // start the trigger thread
                    td.start();
                }
                catch (Exception e)
                {
                    throw Trace.error(Trace.UNKNOWN_FUNCTION, e.ToString());
                }
            }

            database.schemaManager.registerTriggerName(triggerName, t.getName());

            // --
        }

        private Column processCreateColumn()
        {

            string token = tokenizer.getSimpleName();
            bool isQuoted = tokenizer.wasQuotedIdentifier();
            HsqlNameManager.HsqlName hsqlName = database.nameManager.newHsqlName(token, isQuoted);

            return processCreateColumn(hsqlName);
        }

        /**
         *  Responsible for handling the creation of table columns during the
         *  process of executing CREATE TABLE DDL statements.
         *
         *  @param  hsqlName name of the column
         *  @return a Column object with indicated attributes
         *  @throws  HsqlException
         */
        private Column processCreateColumn(HsqlNameManager.HsqlName hsqlName)
        {

            bool isIdentity = false;
            long identityStart = database.firstIdentity;
            long identityIncrement = 1;
            bool isPrimaryKey = false;
            string typeName;
            int type;
            int length = 0;
            int scale = 0;
            bool hasLength = false;
            bool isNullable = true;
            Expression defaultExpr = null;
            string token;

            typeName = tokenizer.getSimpleToken();
            type = Types.getTypeNr(typeName);

            if (type == Types.CHAR)
            {
                if (tokenizer.isGetThis(Token.T_VARYING))
                {
                    type = Types.VARCHAR;
                }
            }

            if (typeName.Equals(Token.T_IDENTITY))
            {
                isIdentity = true;
                isPrimaryKey = true;
            }

            // fredt - when SET IGNORECASE is _in effect, all new VARCHAR columns are defined as VARCHAR_IGNORECASE
            if (type == Types.DOUBLE)
            {
                tokenizer.isGetThis(Token.T_PRECISION);
            }

            if (tokenizer.isGetThis(Token.T_OPENBRACKET))
            {
                hasLength = true;
                length = tokenizer.getInt();

                Trace.check(Types.acceptsPrecisionCreateParam(type),
                            Trace.UNEXPECTED_TOKEN);

                if (type != Types.TIMESTAMP && type != Types.TIME && length == 0)
                {
                    throw Trace.error(Trace.INVALID_SIZE_PRECISION);
                }

                if (tokenizer.isGetThis(Token.T_COMMA))
                {
                    Trace.check(Types.acceptsScaleCreateParam(type),
                                Trace.UNEXPECTED_TOKEN);

                    scale = tokenizer.getInt();
                }

                tokenizer.getThis(Token.T_CLOSEBRACKET);
            }
            else if (type == Types.CHAR && database.sqlEnforceStrictSize)
            {
                length = 1;
            }
            else if (type == Types.VARCHAR && database.sqlEnforceStrictSize)
            {
                throw Trace.error(Trace.COLUMN_SIZE_REQUIRED);
            }

            /**
             * @todo fredt - drop support for SET IGNORECASE and replace the
             * type name with a qualifier specifying the case sensitivity of VARCHAR
             */
            if (type == Types.VARCHAR && database.isIgnoreCase())
            {
                type = Types.VARCHAR_IGNORECASE;
            }

            if (type == Types.FLOAT && length > 53)
            {
                throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
            }

            if (type == Types.TIMESTAMP)
            {
                if (!hasLength)
                {
                    length = 6;
                }
                else if (length != 0 && length != 6)
                {
                    throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
                }
            }

            if (type == Types.TIME)
            {
                if (length != 0)
                {
                    throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
                }
            }

            token = tokenizer.getSimpleToken();

            if (token.Equals(Token.T_DEFAULT))
            {
                defaultExpr = processCreateDefaultExpression(type, length, scale);
                token = tokenizer.getSimpleToken();
            }
            else if (token.Equals(Token.T_GENERATED))
            {
                tokenizer.getThis(Token.T_BY);
                tokenizer.getThis(Token.T_DEFAULT);
                tokenizer.getThis(Token.T_AS);
                tokenizer.getThis(Token.T_IDENTITY);

                if (tokenizer.isGetThis(Token.T_OPENBRACKET))
                {
                    tokenizer.getThis(Token.T_START);
                    tokenizer.getThis(Token.T_WITH);

                    identityStart = tokenizer.getBigint();

                    if (tokenizer.isGetThis(Token.T_COMMA))
                    {
                        tokenizer.getThis(Token.T_INCREMENT);
                        tokenizer.getThis(Token.T_BY);

                        identityIncrement = tokenizer.getBigint();
                    }

                    tokenizer.getThis(Token.T_CLOSEBRACKET);
                }

                isIdentity = true;
                isPrimaryKey = true;
                token = tokenizer.getSimpleToken();
            }

            // fredt@users - accept IDENTITY before or after NOT NULL
            if (token.Equals(Token.T_IDENTITY))
            {
                isIdentity = true;
                isPrimaryKey = true;
                token = tokenizer.getSimpleToken();
            }

            if (token.Equals(Token.T_NULL))
            {
                token = tokenizer.getSimpleToken();
            }
            else if (token.Equals(Token.T_NOT))
            {
                tokenizer.getThis(Token.T_NULL);

                isNullable = false;
                token = tokenizer.getSimpleToken();
            }

            if (token.Equals(Token.T_IDENTITY))
            {
                if (isIdentity)
                {
                    throw Trace.error(Trace.SECOND_PRIMARY_KEY, Token.T_IDENTITY);
                }

                isIdentity = true;
                isPrimaryKey = true;
                token = tokenizer.getSimpleToken();
            }

            if (token.Equals(Token.T_PRIMARY))
            {
                tokenizer.getThis(Token.T_KEY);

                isPrimaryKey = true;
            }
            else
            {
                tokenizer.back();
            }

            // make sure IDENTITY and DEFAULT are not used together
            if (isIdentity && defaultExpr != null)
            {
                throw Trace.error(Trace.UNEXPECTED_TOKEN, Token.T_DEFAULT);
            }

            Column column = new Column(hsqlName, isNullable, type, length, scale,
                                       isPrimaryKey, defaultExpr);

            column.setIdentity(isIdentity, identityStart, identityIncrement);

            return column;
        }

        /**
         * @param type data type of column
         * @param length maximum length of column
         * @ 
         * @return new Expression
         */
        private Expression processCreateDefaultExpression(int type, int length,
                int scale)
        {

            if (type == Types.OTHER)
            {
                throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
            }

            Parser parser = new Parser(session, database, tokenizer);
            Expression expr = parser.readDefaultClause(type);

            expr.resolveTypes(session);

            int newType = expr.getType();

            if (newType == Expression.VALUE || newType == Expression.TRUE
                    || newType == Expression.FALSE
                    || (newType == Expression.FUNCTION
                        && expr.function.isSimple))
            {
                Object defValTemp;

                try
                {
                    defValTemp = expr.getValue(session, type);
                }
                catch (HsqlException )
                {
                    throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
                }

                if (defValTemp != null && database.sqlEnforceStrictSize)
                {
                    try
                    {
                        Column.enforceSize(defValTemp, type, length, scale, true);
                    }
                    catch (HsqlException )
                    {

                        // default value is too long for fixed size column
                        throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
                    }
                }

                return expr;
            }

            throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
        }

        public static void checkboolDefault(string s,
                                               int type)
        {

            if (type != Types.BOOLEAN || s == null)
            {
                return;
            }

            s = s.ToUpper();

            if (s.Equals(Token.T_TRUE) || s.Equals(Token.T_FALSE))
            {
                return;
            }

            if (s.Equals("0") || s.Equals("1"))
            {
                return;
            }

            throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE, s);
        }

        /**
         * Responsible for handling constraints section of CREATE TABLE ...
         *
         * @param t table
         * @param constraint CONSTRAINT keyword used
         * @param primarykeycolumn primary columns
         * @ 
         * @return list of constraints
         */
        private List<Constraint> processCreateConstraints(Table t,
                bool constraint, int[] primarykeycolumn)
        {

            string token;
            List<Constraint> tcList;
            Constraint tempConst;
            HsqlNameManager.HsqlName pkHsqlName;

            // fredt@users 20020225 - comment
            // HSQLDB relies on primary index to be the first one defined
            // and needs original or system added primary key before any
            // non-unique index is created
            tcList = new List<Constraint>();
            tempConst = new Constraint(null, primarykeycolumn, null, null,
                                       Constraint.MAIN, Constraint.NO_ACTION,
                                       Constraint.NO_ACTION);

            // tony_lai@users 20020820 - patch 595099
            pkHsqlName = null;

            tcList.Add(tempConst);

            if (!constraint)
            {
                return tcList;
            }

            while (true)
            {
                HsqlNameManager.HsqlName cname = null;

                if (tokenizer.isGetThis(Token.T_CONSTRAINT))
                {
                    token = tokenizer.getName();

                    string constraintSchema = tokenizer.getLongNameFirst();

                    if (constraintSchema != null)
                    {
                        constraintSchema = session.getSchemaNameForWrite(
                            tokenizer.getLongNameFirst());

                        if (!t.getSchemaName().Equals(constraintSchema))
                        {
                            throw Trace.error(
                                Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS,
                                constraintSchema);
                        }
                    }

                    cname = database.nameManager.newHsqlName(token,
                            tokenizer.wasQuotedIdentifier());
                }

                token = tokenizer.getSimpleToken();

                switch (Token.get(token))
                {

                    case Token.PRIMARY:
                        {
                            tokenizer.getThis(Token.T_KEY);

                            // tony_lai@users 20020820 - patch 595099
                            pkHsqlName = cname;

                            int[] cols = processColumnList(t, false);
                            Constraint mainConst;

                            mainConst = (Constraint)tcList[0];

                            if (mainConst.core.mainColArray != null)
                            {
                                if (!ArrayUtil.areEqual(mainConst.core.mainColArray,
                                                        cols, cols.Length, true))
                                {
                                    throw Trace.error(Trace.SECOND_PRIMARY_KEY);
                                }
                            }

                            mainConst.core.mainColArray = cols;
                            mainConst.constName = pkHsqlName;

                            break;
                        }
                    case Token.UNIQUE:
                        {
                            int[] col = processColumnList(t, false);

                            if (cname == null)
                            {
                                cname = database.nameManager.newAutoName("CT");
                            }

                            tempConst = new Constraint(cname, col, null, null,
                                                       Constraint.UNIQUE,
                                                       Constraint.NO_ACTION,
                                                       Constraint.NO_ACTION);

                            tcList.Add(tempConst);

                            break;
                        }
                    case Token.FOREIGN:
                        {
                            tokenizer.getThis(Token.T_KEY);

                            tempConst = processCreateFK(t, cname);

                            if (tempConst.core.refColArray == null)
                            {
                                Constraint mainConst = (Constraint)tcList[0];

                                tempConst.core.refColArray =
                                    mainConst.core.mainColArray;

                                if (tempConst.core.refColArray == null)
                                {
                                    throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
                                                      Trace.TABLE_HAS_NO_PRIMARY_KEY);
                                }
                            }

                            checkFKColumnDefaults(t, tempConst);
                            t.checkColumnsMatch(tempConst.core.mainColArray,
                                                tempConst.core.refTable,
                                                tempConst.core.refColArray);
                            tcList.Add(tempConst);

                            break;
                        }
                    case Token.CHECK:
                        {
                            if (cname == null)
                            {
                                cname = database.nameManager.newAutoName("CT");
                            }

                            tempConst = new Constraint(cname, null, null, null,
                                                       Constraint.CHECK,
                                                       Constraint.NO_ACTION,
                                                       Constraint.NO_ACTION);

                            processCreateCheckConstraintCondition(tempConst);
                            tcList.Add(tempConst);

                            break;
                        }
                }

                token = tokenizer.getSimpleToken();

                if (token.Equals(Token.T_COMMA))
                {
                    continue;
                }

                if (token.Equals(Token.T_CLOSEBRACKET))
                {
                    break;
                }

                throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
            }

            return tcList;
        }

        /**
         * Responsible for handling check constraints section of CREATE TABLE ...
         *
         * @param c check constraint
         * @ 
         */
        private void processCreateCheckConstraintCondition(Constraint c)
        {

            tokenizer.getThis(Token.T_OPENBRACKET);

            Parser parser = new Parser(session, database, tokenizer);
            Expression condition = parser.parseExpression();

            tokenizer.getThis(Token.T_CLOSEBRACKET);

            c.core.check = condition;
        }

        /**
         * Responsible for handling the execution CREATE TABLE SQL statements.
         *
         * @param type Description of the Parameter
         * @ 
         */
        private void processCreateTable(int type)
        {

            string token = tokenizer.getName();
            HsqlNameManager.HsqlName schemaname =
                session.getSchemaHsqlNameForWrite(tokenizer.getLongNameFirst());

            database.schemaManager.checkUserTableNotExists(session, token,
                    schemaname.name);

            bool isnamequoted = tokenizer.wasQuotedIdentifier();
            int[] pkCols = null;
            int colIndex = 0;
            bool constraint = false;
            Table t = newTable(type, token, isnamequoted, schemaname);

            tokenizer.getThis(Token.T_OPENBRACKET);

            while (true)
            {
                token = tokenizer.getString();

                switch (Token.get(token))
                {

                    case Token.CONSTRAINT:
                    case Token.PRIMARY:
                    case Token.FOREIGN:
                    case Token.UNIQUE:
                    case Token.CHECK:

                        // fredt@users : check for quoted reserved words used as column names
                        constraint = !tokenizer.wasQuotedIdentifier()
                                     && !tokenizer.wasLongName();
                        break;
                }

                tokenizer.back();

                if (constraint)
                {
                    break;
                }

                Column newcolumn = processCreateColumn();

                t.addColumn(newcolumn);

                if (newcolumn.isPrimaryKey())
                {
                    Trace.check(pkCols == null, Trace.SECOND_PRIMARY_KEY,
                                newcolumn.columnName.name);

                    pkCols = new int[] { colIndex };
                }

                token = tokenizer.getSimpleToken();

                if (token.Equals(Token.T_COMMA))
                {
                    colIndex++;

                    continue;
                }

                if (token.Equals(Token.T_CLOSEBRACKET))
                {
                    break;
                }

                throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
            }

            List<Constraint> tempConstraints = processCreateConstraints(t,
                constraint, pkCols);

            if (tokenizer.isGetThis(Token.T_ON))
            {
                if (!t.isTemp())
                {
                    throw Trace.error(Trace.UNEXPECTED_TOKEN, Token.T_ON);
                }

                tokenizer.getThis(Token.T_COMMIT);

                token = tokenizer.getSimpleToken();

                if (token.Equals(Token.T_DELETE)) { }
                else if (token.Equals(Token.T_PRESERVE))
                {
                    t.onCommitPreserve = true;
                }
                else
                {
                    throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                }

                tokenizer.getThis(Token.T_ROWS);
            }

            try
            {
                session.commit();

                Constraint primaryConst = (Constraint)tempConstraints[0];

                t.createPrimaryKey(null, primaryConst.core.mainColArray, true);

                if (primaryConst.core.mainColArray != null)
                {
                    if (primaryConst.constName == null)
                    {
                        primaryConst.constName = t.makeSysPKName();
                    }

                    Constraint newconstraint =
                        new Constraint(primaryConst.constName, t,
                                       t.getPrimaryIndex(),
                                       Constraint.PRIMARY_KEY);

                    t.addConstraint(newconstraint);
                    database.schemaManager.registerConstraintName(
                        primaryConst.constName.name, t.getName());
                }

                for (int i = 1; i < tempConstraints.Count; i++)
                {
                    Constraint tempConst = (Constraint)tempConstraints[(i)];

                    if (tempConst.constType == Constraint.UNIQUE)
                    {
                        TableWorks tableWorks = new TableWorks(session, t);

                        tableWorks.createUniqueConstraint(
                            tempConst.core.mainColArray, tempConst.constName);

                        t = tableWorks.getTable();
                    }

                    if (tempConst.constType == Constraint.FOREIGN_KEY)
                    {
                        TableWorks tableWorks = new TableWorks(session, t);

                        tableWorks.createForeignKey(tempConst.core.mainColArray,
                                                    tempConst.core.refColArray,
                                                    tempConst.constName,
                                                    tempConst.core.refTable,
                                                    tempConst.core.deleteAction,
                                                    tempConst.core.updateAction);

                        t = tableWorks.getTable();
                    }

                    if (tempConst.constType == Constraint.CHECK)
                    {
                        TableWorks tableWorks = new TableWorks(session, t);

                        tableWorks.createCheckConstraint(tempConst,
                                                         tempConst.constName);

                        t = tableWorks.getTable();
                    }
                }

                database.schemaManager.linkTable(t);
            }
            catch (HsqlException e)
            {

                // fredt@users 20020225 - comment
                // if a HsqlException is thrown while creating table, any foreign key that has
                // been created leaves it modification to the expTable _in place
                // need to undo those modifications. This should not happen _in practice.
                database.schemaManager.removeExportedKeys(t);
                database.schemaManager.removeIndexNames(t.tableName);
                database.schemaManager.removeConstraintNames(t.tableName);

                throw e;
            }
        }

        // fredt@users 20020221 - patch 520213 by boucherb@users - self reference FK
        // allows foreign keys that reference a column _in the same table

        /**
         * @param t table
         * @param cname foreign key name
         * @ 
         * @return constraint
         */
        private Constraint processCreateFK(Table t,
                                           HsqlNameManager.HsqlName cname)
        {

            int[] localcol;
            int[] expcol;
            string expTableName;
            Table expTable;
            string token;

            localcol = processColumnList(t, false);

            tokenizer.getThis(Token.T_REFERENCES);

            expTableName = tokenizer.getName();

            string constraintSchema = tokenizer.getLongNameFirst();

            if (constraintSchema != null)
            {
                constraintSchema =
                    session.getSchemaNameForWrite(tokenizer.getLongNameFirst());

                if (!t.getSchemaName().Equals(constraintSchema))
                {
                    throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS,
                                      constraintSchema);
                }
            }

            if (t.getName().name.Equals(expTableName))
            {
                expTable = t;
            }
            else
            {
                expTable = database.schemaManager.getTable(session, expTableName,
                        t.getSchemaName());
            }

            expcol = null;
            token = tokenizer.getSimpleToken();

            tokenizer.back();

            if (token.Equals(Token.T_OPENBRACKET))
            {
                expcol = processColumnList(expTable, false);
            }
            else
            {
                if (expTable.getPrimaryKey() == null)
                {

                    // getPrimaryKey() == null is true while creating the table
                    // fredt - FK statement is part of CREATE TABLE and is self-referencing
                    // reference must be to same table being created
                    // it is resolved _in the calling method
                    Trace.check(t == expTable, Trace.TABLE_HAS_NO_PRIMARY_KEY);
                }
                else
                {
                    if (expTable.hasPrimaryKey())
                    {
                        expcol = expTable.getPrimaryKey();
                    }
                    else
                    {
                        throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
                                          Trace.TABLE_HAS_NO_PRIMARY_KEY);
                    }
                }
            }

            token = tokenizer.getSimpleToken();

            // -- In a while loop we parse a maximium of two
            // -- "ON" statements following the foreign key
            // -- definition this can be
            // -- ON [UPDATE|DELETE] [NO ACTION|RESTRICT|CASCADE|SET [NULL|DEFAULT]]
            int deleteAction = Constraint.NO_ACTION;
            int updateAction = Constraint.NO_ACTION;

            while (token.Equals(Token.T_ON))
            {
                token = tokenizer.getSimpleToken();

                if (deleteAction == Constraint.NO_ACTION
                        && token.Equals(Token.T_DELETE))
                {
                    token = tokenizer.getSimpleToken();

                    if (token.Equals(Token.T_SET))
                    {
                        token = tokenizer.getSimpleToken();

                        if (token.Equals(Token.T_DEFAULT))
                        {
                            deleteAction = Constraint.SET_DEFAULT;
                        }
                        else if (token.Equals(Token.T_NULL))
                        {
                            deleteAction = Constraint.SET_NULL;
                        }
                        else
                        {
                            throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                        }
                    }
                    else if (token.Equals(Token.T_CASCADE))
                    {
                        deleteAction = Constraint.CASCADE;
                    }
                    else if (token.Equals(Token.T_RESTRICT))
                    {

                        // LEGACY compatibility/usability
                        // - same as NO ACTION or nothing at all
                    }
                    else
                    {
                        tokenizer.matchThis(Token.T_NO);
                        tokenizer.getThis(Token.T_ACTION);
                    }
                }
                else if (updateAction == Constraint.NO_ACTION
                         && token.Equals(Token.T_UPDATE))
                {
                    token = tokenizer.getSimpleToken();

                    if (token.Equals(Token.T_SET))
                    {
                        token = tokenizer.getSimpleToken();

                        if (token.Equals(Token.T_DEFAULT))
                        {
                            updateAction = Constraint.SET_DEFAULT;
                        }
                        else if (token.Equals(Token.T_NULL))
                        {
                            updateAction = Constraint.SET_NULL;
                        }
                        else
                        {
                            throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                        }
                    }
                    else if (token.Equals(Token.T_CASCADE))
                    {
                        updateAction = Constraint.CASCADE;
                    }
                    else if (token.Equals(Token.T_RESTRICT))
                    {

                        // LEGACY compatibility/usability
                        // - same as NO ACTION or nothing at all
                    }
                    else
                    {
                        tokenizer.matchThis(Token.T_NO);
                        tokenizer.getThis(Token.T_ACTION);
                    }
                }
                else
                {
                    throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                }

                token = tokenizer.getSimpleToken();
            }

            tokenizer.back();

            if (cname == null)
            {
                cname = database.nameManager.newAutoName("FK");
            }

            return new Constraint(cname, localcol, expTable, expcol,
                                  Constraint.FOREIGN_KEY, deleteAction,
                                  updateAction);
        }

        /**
         * Responsible for handling the execution CREATE VIEW SQL statements.
         *
         * @ 
         */
        private void processCreateView()
        {

            string name = tokenizer.getName();
            HsqlNameManager.HsqlName schemaname =
                session.getSchemaHsqlNameForWrite(tokenizer.getLongNameFirst());
            int logposition = tokenizer.getPartMarker();

            database.schemaManager.checkUserViewNotExists(session, name,
                    schemaname.name);

            HsqlNameManager.HsqlName viewHsqlName = database.nameManager.newHsqlName(name,
                tokenizer.wasQuotedIdentifier());

            viewHsqlName.schema = schemaname;

            HsqlNameManager.HsqlName[] colList = null;

            if (tokenizer.isGetThis(Token.T_OPENBRACKET))
            {
                try
                {
                    List<object> list = Parser.getColumnNames(database, null,
                        tokenizer, true);

                    colList = new HsqlNameManager.HsqlName[list.Count];
                    int i=0;
                    foreach (var n in list)
                    {
                        colList[i++] = (HsqlNameManager.HsqlName)n;
                    }
                }
                catch (HsqlException e)
                {

                    // fredt - a bug _in 1.8.0.0 and previous versions causes view
                    // definitions to script without double quotes around column names
                    // _in certain cases; the workaround here discards the column
                    // names
                    if (database.isStoredFileAccess()
                            && session.isProcessingScript())
                    {
                        while (true)
                        {
                            string token = tokenizer.getString();

                            if (token.Equals(Token.T_CLOSEBRACKET)
                                    || token.Equals(""))
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        throw e;
                    }
                }
            }

            tokenizer.getThis(Token.T_AS);
            tokenizer.setPartMarker();

            Parser parser = new Parser(session, database, tokenizer);
            int brackets = parser.parseOpenBracketsSelect();
            Select select;

            // accept ORDER BY or ORDRY BY with LIMIT - accept unions
            select = parser.parseSelect(brackets, true, false, true, true);

            if (select.sIntoTable != null)
            {
                throw (Trace.error(Trace.INVALID_IDENTIFIER, Token.INTO));
            }

            select.prepareResult(session);

            View view = new View(session, database, viewHsqlName,
                                 tokenizer.getLastPart(), colList);

            session.commit();
            database.schemaManager.linkTable(view);
            tokenizer.setPartMarker(logposition);
        }

        /**
         * Responsible for handling tail of ALTER TABLE ... RENAME ...
         * @param t table
         * @ 
         */
        private void processAlterTableRename(Table t)
        {

            string schema = t.getSchemaName();
            string newName;
            bool isquoted;

            // ensures that if temp table, it also belongs to this session
            /*
                    if (!t.Equals(session, name)) {
                        throw Trace.error(Trace.TABLE_NOT_FOUND);
                    }
            */
            tokenizer.getThis(Token.T_TO);

            newName = tokenizer.getName();

            string newSchema = tokenizer.getLongNameFirst();

            isquoted = tokenizer.wasQuotedIdentifier();
            newSchema = newSchema == null ? schema
                                          : session.getSchemaNameForWrite(
                                              newSchema);

            if (!schema.Equals(newSchema))
            {
                throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
            }

            database.schemaManager.checkUserTableNotExists(session, newName,
                    schema);
            session.commit();
            session.setScripting(true);
            database.schemaManager.renameTable(session, t, newName, isquoted);
        }

        /**
         * Handles ALTER TABLE statements. <p>
         *
         * ALTER TABLE <name> RENAME TO <newname>
         * ALTER INDEX <name> RENAME TO <newname>
         *
         * ALTER TABLE <name> ADD CONSTRAINT <constname> FOREIGN KEY (<col>, ...)
         * REFERENCE <other table> (<col>, ...) [ON DELETE CASCADE]
         *
         * ALTER TABLE <name> ADD CONSTRAINT <constname> UNIQUE (<col>, ...)
         *
         * @ 
         */
        private void processAlter()
        {

            string token;

            session.checkAdmin();
            session.checkDDLWrite();
            session.setScripting(true);

            token = tokenizer.getSimpleToken();

            switch (Token.get(token))
            {

                case Token.INDEX:
                    {
                        processAlterIndex();

                        break;
                    }
                case Token.SCHEMA:
                    {
                        processAlterSchema();

                        break;
                    }
                case Token.SEQUENCE:
                    {
                        processAlterSequence();

                        break;
                    }
                case Token.TABLE:
                    {
                        processAlterTable();

                        break;
                    }
                case Token.USER:
                    {
                        processAlterUser();

                        break;
                    }
                default:
                    {
                        throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                    }
            }
        }

        /**
         * Handles ALTER TABLE DDL.
         *
         * @ 
         */
        private void processAlterTable()
        {

            string tableName = tokenizer.getName();
            string schema =
                session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
            Table t = database.schemaManager.getUserTable(session, tableName,
                schema);
            string token;

            if (t.isView())
            {
                throw Trace.error(Trace.NOT_A_TABLE);
            }

            session.setScripting(true);

            token = tokenizer.getSimpleToken();

            switch (Token.get(token))
            {

                case Token.RENAME:
                    {
                        processAlterTableRename(t);

                        return;
                    }
                case Token.ADD:
                    {
                        HsqlNameManager.HsqlName cname = null;

                        if (tokenizer.isGetThis(Token.T_CONSTRAINT))
                        {
                            token = tokenizer.getName();

                            string constraintSchema = tokenizer.getLongNameFirst();

                            if (constraintSchema != null)
                            {
                                constraintSchema = session.getSchemaNameForWrite(
                                    tokenizer.getLongNameFirst());

                                if (!t.getSchemaName().Equals(constraintSchema))
                                {
                                    throw Trace.error(
                                        Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS,
                                        constraintSchema);
                                }
                            }

                            cname = database.nameManager.newHsqlName(token,
                                    tokenizer.wasQuotedIdentifier());
                        }

                        token = tokenizer.getString();

                        if (tokenizer.wasQuotedIdentifier()
                                && tokenizer.wasSimpleName())
                        {
                            tokenizer.back();
                            processAlterTableAddColumn(t);

                            return;
                        }

                        if (!tokenizer.wasSimpleToken())
                        {
                            throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                        }

                        switch (Token.get(token))
                        {

                            case Token.FOREIGN:
                                tokenizer.getThis(Token.T_KEY);
                                processAlterTableAddForeignKeyConstraint(t, cname);

                                return;

                            case Token.UNIQUE:
                                processAlterTableAddUniqueConstraint(t, cname);

                                return;

                            case Token.CHECK:
                                processAlterTableAddCheckConstraint(t, cname);

                                return;

                            case Token.PRIMARY:
                                tokenizer.getThis(Token.T_KEY);
                                processAlterTableAddPrimaryKey(t, cname);

                                return;

                            default:
                                if (cname != null)
                                {
                                    throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                                }

                                tokenizer.back();
                                processAlterTableAddColumn(t);
                                break;
                            case Token.COLUMN:
                                processAlterTableAddColumn(t);

                                return;
                        }
                    }
                    break;
                case Token.DROP:
                    {
                        token = tokenizer.getString();

                        if (tokenizer.wasQuotedIdentifier()
                                && tokenizer.wasSimpleName())
                        {
                            tokenizer.back();
                            processAlterTableDropColumn(t);

                            return;
                        }

                        if (!tokenizer.wasSimpleToken())
                        {
                            throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                        }

                        switch (Token.get(token))
                        {

                            case Token.PRIMARY:
                                tokenizer.getThis(Token.T_KEY);

                                if (t.hasPrimaryKey())
                                {
                                    processAlterTableDropConstraint(
                                        t, t.getPrimaryConstraint().getName().name);
                                }
                                else
                                {
                                    throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
                                                      Trace.TABLE_HAS_NO_PRIMARY_KEY,
                                                      new Object[] {
                                "PRIMARY KEY", t.getName().name
                            });
                                }

                                return;
                            
                            case Token.CONSTRAINT:
                                processAlterTableDropConstraint(t);

                                return;

                            default:
                                tokenizer.back();
                                processAlterTableDropColumn(t);
                                return;
           
                            case Token.COLUMN:
                                processAlterTableDropColumn(t);
                                return;
                            
                        }
                    }
           
                case Token.ALTER:
                    {
                        tokenizer.isGetThis(Token.T_COLUMN);
                        processAlterColumn(t);

                        return;
                    }
                default:
                    {
                        throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                    }
            }
        }

        /**
         * Handles ALTER COLUMN
         *
         * @param t table
         * @ 
         */
        private void processAlterColumn(Table t)
        {

            string columnName = tokenizer.getSimpleName();
            int columnIndex = t.getColumnNr(columnName);
            Column column = t.getColumn(columnIndex);
            string token = tokenizer.getSimpleToken();

            switch (Token.get(token))
            {

                case Token.RENAME:
                    {
                        tokenizer.getThis(Token.T_TO);
                        processAlterColumnRename(t, column);

                        return;
                    }
                case Token.DROP:
                    {
                        tokenizer.getThis(Token.T_DEFAULT);

                        TableWorks tw = new TableWorks(session, t);

                        tw.setColDefaultExpression(columnIndex, null);

                        return;
                    }
                case Token.SET:
                    {

                        //4-8-2005 MarcH and HuugO ALTER TABLE <tablename> ALTER COLUMN <column name> SET [NOT] NULL support added
                        token = tokenizer.getSimpleToken();

                        if (token.Equals(Token.T_NOT))
                        {
                            tokenizer.getThis(Token.T_NULL);

                            TableWorks tw = new TableWorks(session, t);

                            tw.setColNullability(column, false);
                        }
                        else if (token.Equals(Token.T_NULL))
                        {
                            TableWorks tw = new TableWorks(session, t);

                            tw.setColNullability(column, true);
                        }
                        else if (token.Equals(Token.T_DEFAULT))
                        {

                            //alter table alter column set default
                            TableWorks tw = new TableWorks(session, t);
                            int type = column.getType();
                            int length = column.getSize();
                            int scale = column.getScale();
                            Expression expr = processCreateDefaultExpression(type,
                                length, scale);

                            tw.setColDefaultExpression(columnIndex, expr);
                        }
                        else
                        {
                            throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                        }

                        return;
                    }
                case Token.RESTART:
                    {
                        tokenizer.getThis(Token.T_WITH);

                        long identityStart = tokenizer.getBigint();
                        int id = t.getIdentityColumn();

                        if (id == -1)
                        {
                            throw Trace.error(Trace.OPERATION_NOT_SUPPORTED);
                        }

                        t.identitySequence.reset(identityStart);

                        return;
                    }
                default:
                    {
                        tokenizer.back();
                        processAlterColumnType(t, column);
                        break;
                    }
            }
        }

        private void processAlterColumnType(Table table,
                                            Column oldCol)
        {

            Column newCol = processCreateColumn(oldCol.columnName);
            TableWorks tw = new TableWorks(session, table);

            tw.reTypeColumn(oldCol, newCol);
        }

        /**
         * Responsible for handling tail of ALTER COLUMN ... RENAME ...
         * @param t table
         * @param column column
         * @ 
         */
        private void processAlterColumnRename(Table t,
                                              Column column)
        {

            string newName = tokenizer.getSimpleName();
            bool isquoted = tokenizer.wasQuotedIdentifier();

            if (t.findColumn(newName) > -1)
            {
                throw Trace.error(Trace.COLUMN_ALREADY_EXISTS, newName);
            }

            t.database.schemaManager.checkColumnIsInView(t,
                    column.columnName.name);
            session.commit();
            session.setScripting(true);
            t.renameColumn(column, newName, isquoted);
        }

        /**
         * Handles ALTER INDEX.
         *
         * @ 
         */
        private void processAlterIndex()
        {

            // only the one supported operation, so far
            processAlterIndexRename();
        }

        private void processAlterSchema()
        {

            // only the one supported operation, so far
            processAlterSchemaRename();
        }

        /**
         * Responsible for handling parse and execute of SQL DROP DDL
         *
         * @throws  HsqlException
         */
        private void processDrop()
        {

            string token;
            bool isview;

            session.checkReadWrite();
            session.checkAdmin();
            session.setScripting(true);

            token = tokenizer.getSimpleToken();
            isview = false;

            switch (Token.get(token))
            {

                case Token.INDEX:
                    {
                        processDropIndex();

                        break;
                    }
                case Token.SCHEMA:
                    {
                        processDropSchema();

                        break;
                    }
                case Token.SEQUENCE:
                    {
                        processDropSequence();

                        break;
                    }
                case Token.TRIGGER:
                    {
                        processDropTrigger();

                        break;
                    }
                case Token.USER:
                    {
                        processDropUser();

                        break;
                    }
                case Token.ROLE:
                    {
                        database.getGranteeManager().dropRole(
                            tokenizer.getSimpleName());

                        break;
                    }
                case Token.VIEW:
                    {
                        isview = true;
                        processDropTable(isview);
                    }    //fall thru
                    break;
                case Token.TABLE:
                    {
                        processDropTable(isview);

                        break;
                    }
                default:
                    {
                        throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                    }
            }
        }

        /**
         *  Responsible for handling the execution of GRANT and REVOKE SQL
         *  statements.
         *
         * @param grant true if grant, false if revoke
         * @ 
         */
        private void processGrantOrRevoke(bool grant)
        {

            int right;
            Object accessKey;
            string token;

            session.checkAdmin();
            session.checkDDLWrite();
            session.setScripting(true);

            right = 0;
            token = tokenizer.getSimpleToken();

            tokenizer.back();

            if (!GranteeManager.validRightString(token))
            {
                processRoleGrantOrRevoke(grant);

                return;
            }

            do
            {
                token = tokenizer.getSimpleToken();
                right |= GranteeManager.getCheckRight(token);
            } while (tokenizer.isGetThis(Token.T_COMMA));

            tokenizer.getThis(Token.T_ON);

            accessKey = null;

            if (tokenizer.isGetThis(Token.T_CLASS))
            {
                accessKey = tokenizer.getSimpleName();

                if (!tokenizer.wasQuotedIdentifier())
                {
                    throw Trace.error(Trace.QUOTED_IDENTIFIER_REQUIRED);
                }
            }
            else
            {
                token = tokenizer.getName();

                string schema =
                    session.getSchemaName(tokenizer.getLongNameFirst());
                Table t = database.schemaManager.getTable(session, token, schema);

                accessKey = t.getName();

                session.setScripting(true);
            }

            tokenizer.getThis(grant ? Token.T_TO
                                    : Token.T_FROM);

            token = getUserIdentifier();

            GranteeManager gm = database.getGranteeManager();

            if (grant)
            {
                gm.grant(token, accessKey, right);
            }
            else
            {
                gm.revoke(token, accessKey, right);
            }
        }

        /**
         * Responsible for handling CONNECT
         *
         * @ 
         */
        private void processConnect()
        {

            string userName;
            string password;
            User user;

            tokenizer.getThis(Token.T_USER);

            userName = getUserIdentifier();

            if (tokenizer.isGetThis(Token.T_PASSWORD))
            {

                // legacy log statement or connect statement issued by user
                password = getPassword();
                user = database.getUserManager().getUser(userName, password);

                session.commit();
                session.setUser(user);
                database.logger.logConnectUser(session);
            }
            else if (session._isProcessingLog)
            {

                // processing log statement
                // do not change the user, as isSys() must remain true when processing log
                session.commit();
            }
            else
            {

                // force throw if not log statement
                tokenizer.getThis(Token.T_PASSWORD);
            }
        }

        /**
         * Responsible for handling the execution of SET SQL statements
         *
         * @throws  HsqlException
         */
        private void processSet()
        {

            string token;

            session.setScripting(true);

            token = tokenizer.getSimpleToken();

            switch (Token.get(token))
            {

                case Token.PROPERTY:
                    {
                        HsqlDatabaseProperties p;

                        session.checkAdmin();

                        token = tokenizer.getSimpleName();

                        if (!tokenizer.wasQuotedIdentifier())
                        {
                            throw Trace.error(Trace.QUOTED_IDENTIFIER_REQUIRED);
                        }

                        p = database.getProperties();

                        bool isbool = p.isbool(token);
                        bool isintegral = p.isIntegral(token);
                        bool isstring = p.isString(token);

                        Trace.check(isbool || isintegral || isstring,
                                    Trace.ACCESS_IS_DENIED, token);

                        int type = isbool ? Types.BOOLEAN
                                                 : isintegral ? Types.INTEGER
                                                              : Types.VARCHAR;
                        Object value = tokenizer.getInType(type);

                        if (HsqlDatabaseProperties.hsqldb_cache_file_scale.Equals(
                                token))
                        {
                            if (database.logger.hasCache()
                                    || ((int)value) != 8)
                            {
                                Trace.throwerror(Trace.ACCESS_IS_DENIED, token);
                            }
                        }

                        p.setDatabaseProperty(token, value.ToString().ToLower());
                        p.setDatabaseVariables();

                        break;
                    }
                case Token.SCHEMA:
                    {
                        session.setScripting(false);
                        session.setSchema(tokenizer.getSimpleName());

                        break;
                    }
                case Token.PASSWORD:
                    {
                        session.checkDDLWrite();
                        session.getUser().setPassword(getPassword());

                        break;
                    }
                case Token.READONLY:
                    {
                        session.commit();
                        session.setReadOnly(processTrueOrFalse());

                        break;
                    }
                case Token.LOGSIZE:
                    {
                        session.checkAdmin();
                        session.checkDDLWrite();

                        int i = tokenizer.getInt();

                        database.logger.setLogSize(i);

                        break;
                    }
                case Token.SCRIPTFORMAT:
                    {
                        session.checkAdmin();
                        session.checkDDLWrite();
                        session.setScripting(false);

                        token = tokenizer.getSimpleToken();

                        int i = ArrayUtil.find(ScriptWriterBase.LIST_SCRIPT_FORMATS,
                                               token);

                        if (i == 0 || i == 1 || i == 3)
                        {
                            database.logger.setScriptType(i);
                        }
                        else
                        {
                            throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                        }

                        break;
                    }
                case Token.IGNORECASE:
                    {
                        session.checkAdmin();
                        session.checkDDLWrite();
                        database.setIgnoreCase(processTrueOrFalse());

                        break;
                    }
                case Token.MAXROWS:
                    {
                        session.setScripting(false);

                        int i = tokenizer.getInt();

                        session.setSQLMaxRows(i);

                        break;
                    }
                case Token.AUTOCOMMIT:
                    {
                        session.setAutoCommit(processTrueOrFalse());

                        break;
                    }
                case Token.TABLE:
                    {
                        session.checkAdmin();
                        session.checkDDLWrite();

                        token = tokenizer.getName();

                        string schema = session.getSchemaNameForWrite(
                            tokenizer.getLongNameFirst());
                        Table t = database.schemaManager.getTable(session, token,
                            schema);

                        token = tokenizer.getSimpleToken();

                        session.setScripting(true);

                        switch (Token.get(token))
                        {

                            default:
                                {
                                    throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                                }
                            case Token.SOURCE:
                                {
                                    session.checkAdmin();

                                    // SET TABLE <table> SOURCE HEADER
                                    if (tokenizer.isGetThis(Token.T_HEADER))
                                    {
                                        token = tokenizer.getString();

                                        if (!tokenizer.wasQuotedIdentifier())
                                        {
                                            throw Trace.error(Trace.TEXT_TABLE_SOURCE);
                                        }

                                        try
                                        {
                                            t.setHeader(token);
                                        }
                                        catch (Exception e)
                                        {
                                            if (session.isProcessingLog()
                                                    || session.isProcessingScript())
                                            {
                                                HsqlException warning =
                                                    Trace.error(Trace.GENERAL_IO_ERROR,
                                                                e.Message);

                                                session.addWarning(warning);

                                                // add an entry to applog too
                                            }
                                            else
                                            {
                                                if (e is HsqlException)
                                                {
                                                    throw (HsqlException)e;
                                                }
                                                else
                                                {
                                                    throw Trace.error(
                                                        Trace.GENERAL_IO_ERROR,
                                                        e.Message);
                                                }
                                            }
                                        }

                                        break;
                                    }

                                    // SET TABLE <table> SOURCE ON
                                    if (tokenizer.isGetThis(Token.T_ON))
                                    {
                                        t.connect(session);
                                        database.setMetaDirty(false);

                                        break;
                                    }

                                    // SET TABLE <table> SOURCE OFF
                                    if (tokenizer.isGetThis(Token.T_OFF))
                                    {
                                        t.disconnect(session);
                                        database.setMetaDirty(false);

                                        break;
                                    }

                                    // SET TABLE <table> SOURCE <source>
                                    token = tokenizer.getString();

                                    if (!tokenizer.wasQuotedIdentifier())
                                    {
                                        throw Trace.error(Trace.TEXT_TABLE_SOURCE);
                                    }

                                    bool isDesc = false;

                                    isDesc = tokenizer.isGetThis(Token.T_DESC);

                                    try
                                    {
                                        t.setDataSource(session, token, isDesc, false);
                                    }
                                    catch (Exception e)
                                    {
                                        if (session.isProcessingLog()
                                                || session.isProcessingScript())
                                        {
                                            HsqlException warning =
                                                Trace.error(Trace.GENERAL_IO_ERROR,
                                                            e.Message);

                                            session.addWarning(warning);

                                            // add an entry to applog too
                                        }
                                        else
                                        {
                                            if (e is HsqlException)
                                            {
                                                throw (HsqlException)e;
                                            }
                                            else
                                            {
                                                throw Trace.error(Trace.GENERAL_IO_ERROR,
                                                                  e.Message);
                                            }
                                        }
                                    }

                                    break;
                                }
                            case Token.READONLY:
                                {
                                    session.checkAdmin();
                                    t.setDataReadOnly(processTrueOrFalse());
                                    database.setMetaDirty(false);

                                    break;
                                }
                            case Token.INDEX:
                                {
                                    session.checkAdmin();

                                    string roots =
                                        (String)tokenizer.getInType(Types.VARCHAR);

                                    t.setIndexRoots(roots);

                                    break;
                                }
                        }

                        break;
                    }
                case Token.REFERENTIAL_INTEGRITY:
                    {
                        session.checkAdmin();
                        session.checkDDLWrite();
                        session.setScripting(false);
                        database.setReferentialIntegrity(processTrueOrFalse());

                        break;
                    }
                case Token.CHECKPOINT:
                    {
                        session.checkAdmin();
                        session.checkDDLWrite();
                        tokenizer.getThis(Token.T_DEFRAG);

                        int size = tokenizer.getInt();

                        database.getProperties().setProperty(
                            HsqlDatabaseProperties.hsqldb_defrag_limit, size);

                        break;
                    }
                case Token.WRITE_DELAY:
                    {
                        session.checkAdmin();
                        session.checkDDLWrite();

                        int delay = 0;

                        tokenizer.getString();

                        Object value = tokenizer.getAsValue();

                        if (tokenizer.getType() == Types.INTEGER)
                        {
                            delay = ((int)value);
                        }
                        else if (true.Equals(value))
                        {
                            delay = database.getProperties().getDefaultWriteDelay();
                        }
                        else if (false.Equals(value))
                        {
                            delay = 0;
                        }
                        else
                        {
                            throw Trace.error(Trace.UNEXPECTED_TOKEN);
                        }

                        if (!tokenizer.isGetThis("MILLIS"))
                        {
                            delay *= 1000;
                        }

                        database.logger.setWriteDelay(delay);

                        break;
                    }
                case Token.DATABASE:
                    {
                        session.checkAdmin();
                        session.checkDDLWrite();
                        tokenizer.getThis(Token.T_COLLATION);

                        string cname = tokenizer.getSimpleName();

                        if (!tokenizer.wasQuotedIdentifier())
                        {
                            throw Trace.error(Trace.INVALID_IDENTIFIER);
                        }

                        database.collation.setCollation(cname);

                        break;
                    }
                default:
                    {
                        throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
                    }
            }
        }

        /**
         * Retrieves bool value corresponding to the next token.
         *
         * @return   true if next token is "TRUE"; false if next token is "FALSE"
         * @throws  HsqlException if the next token is neither "TRUE" or "FALSE"
         */
        private bool processTrueOrFalse()
        {

            string sToken = tokenizer.getSimpleToken();

            if (sToken.Equals(Token.T_TRUE))
            {
                return true;
            }
            else if (sToken.Equals(Token.T_FALSE))
            {
                return false;
            }
            else
            {
                throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
            }
        }

        /**
         * Responsible for  handling the execution of COMMIT [WORK]
         *
         * @throws  HsqlException
         */
        private void processCommit()
        {
            tokenizer.isGetThis(Token.T_WORK);
            session.commit();
        }

        /**
         * Responsible for handling the execution of ROLLBACK SQL statements.
         *
         * @throws  HsqlException
         */
        private void processRollback()
        {

            string token;
            bool toSavepoint;

            token = tokenizer.getSimpleToken();
            toSavepoint = false;

            if (token.Equals(Token.T_WORK))
            {

                // do nothing
            }
            else if (token.Equals(Token.T_TO))
            {
                tokenizer.getThis(Token.T_SAVEPOINT);

                token = tokenizer.getSimpleName();
                toSavepoint = true;
            }
            else
            {
                tokenizer.back();
            }

            if (toSavepoint)
            {
                session.rollbackToSavepoint(token);
            }
            else
            {
                session.rollback();
            }
        }

        /**
         * Responsible for handling the execution of SAVEPOINT SQL statements.
         *
         * @throws  HsqlException
         */
        private void processSavepoint()
        {

            string token;

            token = tokenizer.getSimpleName();

            session.savepoint(token);
        }

        /**
         * Responsible for handling the execution of SHUTDOWN SQL statements
         *
         * @throws  HsqlException
         */
        private void processShutdown()
        {

            int closemode;
            string token;

            // HUH?  We should *NEVER* be able to get here if session is closed
            if (!session.isClosed())
            {
                session.checkAdmin();
            }

            closemode = Database.CLOSEMODE_NORMAL;
            token = tokenizer.getSimpleToken();

            // fredt - todo - catch misspelt qualifiers here and elsewhere
            if (token.Equals(Token.T_IMMEDIATELY))
            {
                closemode = Database.CLOSEMODE_IMMEDIATELY;
            }
            else if (token.Equals(Token.T_COMPACT))
            {
                closemode = Database.CLOSEMODE_COMPACT;
            }
            else if (token.Equals(Token.T_SCRIPT))
            {
                closemode = Database.CLOSEMODE_SCRIPT;
            }
            else if (token.Equals(Token.T_SEMICOLON))
            {

                // only semicolon is accepted here
            }
            else if (token.Length != 0)
            {
                throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
            }

            database.close(closemode);
        }

        /**
         * Responsible for handling CHECKPOINT [DEFRAG].
         *
         * @throws  HsqlException
         */
        private void processCheckpoint()
        {

            bool defrag;
            string token;

            session.checkAdmin();
            session.checkDDLWrite();

            defrag = false;
            token = tokenizer.getSimpleToken();

            if (token.Equals(Token.T_DEFRAG))
            {
                defrag = true;
            }
            else if (token.Equals(Token.T_SEMICOLON))
            {

                // only semicolon is accepted here
            }
            else if (token.Length != 0)
            {
                throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
            }

            database.logger.checkpoint(defrag);
        }

        // --------------------- new methods / simplifications ------------------------
        private HsqlNameManager.HsqlName newIndexHsqlName(string name,
                                          bool isQuoted)
        {

            return HsqlNameManager.HsqlName.isReservedName(name)
                   ? database.nameManager.newAutoName("USER", name)
                   : database.nameManager.newHsqlName(name, isQuoted);
        }

        private Table newTable(int type, string name, bool quoted,
                               HsqlNameManager.HsqlName schema)
        {

            HsqlNameManager.HsqlName tableHsqlName = database.nameManager.newHsqlName(name,
                quoted);

            tableHsqlName.schema = schema;

            switch (type)
            {

                case Table.TEMP_TEXT_TABLE:
                case Table.TEXT_TABLE:
                    {
                        return new TextTable(database, tableHsqlName, type);
                    }
                default:
                    {
                        return new Table(database, tableHsqlName, type);
                    }
            }
        }

        /**
         * Checks if the attributes of the Column argument, c, are compatible with
         * the operation of adding such a Column to the Table argument, t.
         *
         * @param t to which to add the Column, c
         * @param c the Column to add to the Table, t
         * @  if the operation of adding the Column, c, to
         *      the table t is not valid
         */
        private void checkAddColumn(Table t, Column c)
        {

            bool canAdd = true;

            if (t.findColumn(c.columnName.name) != -1)
            {
                throw Trace.error(Trace.COLUMN_ALREADY_EXISTS, c.columnName.name);
            }

            if (c.isPrimaryKey() && t.hasPrimaryKey())
            {
                canAdd = false;
            }

            if (canAdd && !t.isEmpty(session))
            {
                canAdd = c.isNullable() || c.getDefaultExpression() != null;
            }

            if (!canAdd)
            {
                throw Trace.error(Trace.BAD_ADD_COLUMN_DEFINITION);
            }
        }

        private void checkFKColumnDefaults(Table t,
                                           Constraint tc)
        {

            bool check = tc.core.updateAction == Constraint.SET_DEFAULT;

            check = check || tc.core.deleteAction == Constraint.SET_DEFAULT;

            if (check)
            {
                int[] localCol = tc.core.mainColArray;

                for (int j = 0; j < localCol.Length; j++)
                {
                    Column column = t.getColumn(localCol[j]);
                    Expression defExpr = column.getDefaultExpression();

                    if (defExpr == null)
                    {
                        string columnName = column.columnName.name;

                        throw Trace.error(Trace.NO_DEFAULT_VALUE_FOR_COLUMN,
                                          new Object[] { columnName });
                    }
                }
            }
        }

        private void processAlterSequence()
        {

            long start;
            string name = tokenizer.getName();
            string schemaname = tokenizer.getLongNameFirst();

            schemaname = session.getSchemaNameForWrite(schemaname);

            tokenizer.getThis(Token.T_RESTART);
            tokenizer.getThis(Token.T_WITH);

            start = tokenizer.getBigint();

            NumberSequence seq = database.schemaManager.getSequence(name,
                schemaname);

            seq.reset(start);
        }

        /**
         * Handles ALTER INDEX &lt;index-name&gt; RENAME.
         *
         * @ 
         */
        private void processAlterIndexRename()
        {

            string name = tokenizer.getName();
            string schema =
                session.getSchemaNameForWrite(tokenizer.getLongNameFirst());

            tokenizer.getThis(Token.T_RENAME);
            tokenizer.getThis(Token.T_TO);

            string newName = tokenizer.getName();
            string newSchema = tokenizer.getLongNameFirst();

            newSchema = newSchema == null ? schema
                                          : session.getSchemaNameForWrite(
                                              newSchema);

            bool isQuoted = tokenizer.wasQuotedIdentifier();

            if (!schema.Equals(newSchema))
            {
                throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
            }

            Table t = database.schemaManager.findUserTableForIndex(session, name,
                schema);

            if (t == null)
            {
                throw Trace.error(Trace.INDEX_NOT_FOUND, name);
            }

            database.schemaManager.checkIndexExists(name, t.getSchemaName(), true);

            if (HsqlNameManager.HsqlName.isReservedName(name))
            {
                throw Trace.error(Trace.SYSTEM_INDEX, name);
            }

            if (HsqlNameManager.HsqlName.isReservedName(newName))
            {
                throw Trace.error(Trace.BAD_INDEX_CONSTRAINT_NAME, newName);
            }

            session.setScripting(true);
            session.commit();
            t.getIndex(name).setName(newName, isQuoted);
            database.schemaManager.renameIndex(name, newName, t.getName());
        }

        /**
         * Handles ALTER SCHEMA ... RENAME TO .
         *
         * @ 
         */
        private void processAlterSchemaRename()
        {

            string name = tokenizer.getSimpleName();

            tokenizer.getThis(Token.T_RENAME);
            tokenizer.getThis(Token.T_TO);

            string newName = tokenizer.getSimpleName();
            bool isQuoted = tokenizer.wasQuotedIdentifier();

            database.schemaManager.renameSchema(name, newName, isQuoted);
        }

        /**
         *
         * @param t table
         * @ 
         */
        private void processAlterTableAddColumn(Table t)
        {

            string token;
            int colindex = t.getColumnCount();
            Column column = processCreateColumn();

            checkAddColumn(t, column);

            if (tokenizer.isGetThis(Token.T_BEFORE))
            {
                token = tokenizer.getSimpleName();
                colindex = t.getColumnNr(token);
            }

            session.commit();

            TableWorks tableWorks = new TableWorks(session, t);

            tableWorks.addColumn(column, colindex);

            return;
        }

        /**
         * Responsible for handling tail of ALTER TABLE ... DROP COLUMN ...
         *
         * @param t table
         * @ 
         */
        private void processAlterTableDropColumn(Table t)
        {

            string token;
            int colindex;

            token = tokenizer.getName();
            colindex = t.getColumnNr(token);

            session.commit();

            TableWorks tableWorks = new TableWorks(session, t);

            tableWorks.dropColumn(colindex);
        }

        /**
         * Responsible for handling tail of ALTER TABLE ... DROP CONSTRAINT ...
         *
         * @param t table
         * @ 
         */
        private void processAlterTableDropConstraint(Table t)
        {
            processAlterTableDropConstraint(t, tokenizer.getName());
        }

        /**
         * Responsible for handling tail of ALTER TABLE ... DROP CONSTRAINT ...
         *
         * @param t table
         * @param name
         * @ 
         */
        private void processAlterTableDropConstraint(Table t,
                string cname)
        {

            session.commit();

            TableWorks tableWorks = new TableWorks(session, t);

            tableWorks.dropConstraint(cname);

            return;
        }

        /**
         * If an invalid alias is encountered while processing an old script,
         * simply discard it.
         */
        private void processCreateAlias()
        {

            string alias;
            string methodFQN;

            try
            {
                alias = tokenizer.getSimpleName();
            }
            catch (HsqlException e)
            {
                if (session.isProcessingScript())
                {
                    alias = null;
                }
                else
                {
                    throw e;
                }
            }

            tokenizer.getThis(Token.T_FOR);

            methodFQN = upgradeMethodFQN(tokenizer.getSimpleName());

            if (alias != null)
            {
                database.getAliasMap()[alias] = methodFQN;
            }
        }

        private void processCreateIndex(bool unique)
        {

            Table t;
            string indexName = tokenizer.getName();
            string schema = tokenizer.getLongNameFirst();
            bool indexNameQuoted = tokenizer.wasQuotedIdentifier();

            tokenizer.getThis(Token.T_ON);

            string tablename = tokenizer.getName();
            string tableschema =
                session.getSchemaNameForWrite(tokenizer.getLongNameFirst());

            if (schema != null && !schema.Equals(tableschema))
            {
                throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
            }

            t = database.schemaManager.getTable(session, tablename, tableschema);

            database.schemaManager.checkIndexExists(indexName, t.getSchemaName(),
                    false);

            HsqlNameManager.HsqlName indexHsqlName = newIndexHsqlName(indexName, indexNameQuoted);
            int[] indexColumns = processColumnList(t, true);
            string extra = tokenizer.getSimpleToken();

            if (!Token.T_DESC.Equals(extra) && !Token.T_ASC.Equals(extra))
            {
                tokenizer.back();
            }

            session.commit();
            session.setScripting(true);

            TableWorks tableWorks = new TableWorks(session, t);

            tableWorks.createIndex(indexColumns, indexHsqlName, unique, false,
                                   false);
        }

        /**
         * limitations _in Tokenizer dictate that initial value or increment must
         * be positive
         * @ 
         */
        private void processCreateSequence()
        {

            /*
                    CREATE SEQUENCE <name>
                    [AS {INTEGER | BIGINT}]
                    [START WITH <value>]
                    [INCREMENT BY <value>]
            */
            int type = Types.INTEGER;
            long increment = 1;
            long start = 0;
            string name = tokenizer.getName();
            bool isquoted = tokenizer.wasQuotedIdentifier();
            HsqlNameManager.HsqlName schemaname =
                session.getSchemaHsqlNameForWrite(tokenizer.getLongNameFirst());

            if (tokenizer.isGetThis(Token.T_AS))
            {
                string typestring = tokenizer.getSimpleToken();

                type = Types.getTypeNr(typestring);

                Trace.check(type == Types.INTEGER || type == Types.BIGINT,
                            Trace.WRONG_DATA_TYPE);
            }

            if (tokenizer.isGetThis(Token.T_START))
            {
                tokenizer.getThis(Token.T_WITH);

                start = tokenizer.getBigint();
            }

            if (tokenizer.isGetThis(Token.T_INCREMENT))
            {
                tokenizer.getThis(Token.T_BY);

                increment = tokenizer.getBigint();
            }

            HsqlNameManager.HsqlName hsqlname = database.nameManager.newHsqlName(name, isquoted);

            hsqlname.schema = schemaname;

            database.schemaManager.createSequence(hsqlname, start, increment,
                                                  type);
        }

        /**
         * CREATE SCHEMA PUBLIC _in scripts should pass this, so we do not throw
         * if this schema is created a second time
         */
        private void processCreateSchema()
        {

            string name = tokenizer.getSimpleName();
            bool isquoted = tokenizer.wasQuotedIdentifier();

            if (session.isSchemaDefintion())
            {
                throw Trace.error(Trace.INVALID_IDENTIFIER);
            }

            tokenizer.getThis(Token.T_AUTHORIZATION);
            tokenizer.getThis(GranteeManager.DBA_ADMIN_ROLE_NAME);

            if (database.schemaManager.schemaExists(name))
            {
                if (!session._isProcessingScript)
                {
                    throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
                }
            }
            else
            {
                database.schemaManager.createSchema(name, isquoted);
            }

            HsqlNameManager.HsqlName schemaName = database.schemaManager.getSchemaHsqlName(name);

            database.logger.writeToLog(session,
                                       DatabaseScript.getSchemaCreateDDL(database,
                                           schemaName));
            database.logger.writeToLog(session,
                                       "SET SCHEMA " + schemaName.statementName);
            session.startSchemaDefinition(name);

            session.loggedSchema = session.currentSchema;
        }

        private void processCreateUser()
        {

            string name;
            string password;
            bool admin;

            name = getUserIdentifier();

            tokenizer.getThis(Token.T_PASSWORD);

            password = getPassword();
            admin = tokenizer.isGetThis(Token.T_ADMIN);

            database.getUserManager().createUser(name, password);

            if (admin)
            {
                database.getGranteeManager().grant(
                    name, GranteeManager.DBA_ADMIN_ROLE_NAME);
            }
        }

        private void processDisconnect()
        {
            session.close();
        }

        private void processDropTable(bool isView)
        {

            bool ifexists = false;
            bool cascade = false;

            if (tokenizer.isGetThis(Token.T_IF))
            {
                tokenizer.getThis(Token.T_EXISTS);

                ifexists = true;
            }

            string name = tokenizer.getName();
            string schema = tokenizer.getLongNameFirst();

            if (tokenizer.isGetThis(Token.T_IF))
            {
                tokenizer.getThis(Token.T_EXISTS);

                ifexists = true;
            }

            cascade = tokenizer.isGetThis(Token.T_CASCADE);

            if (!cascade)
            {
                tokenizer.isGetThis(Token.T_RESTRICT);
            }

            if (ifexists && schema != null
                    && !database.schemaManager.schemaExists(schema))
            {
                return;
            }

            schema = session.getSchemaNameForWrite(schema);

            database.schemaManager.dropTable(session, name, schema, ifexists,
                                             isView, cascade);
        }

        private void processDropUser()
        {

            session.checkAdmin();
            session.checkDDLWrite();

            string userName = getPassword();

            if (database.getSessionManager().isUserActive(userName))
            {

                // todo - new error message "cannot drop a user that is currently connected."    // NOI18N
                throw Trace.error(Trace.ACCESS_IS_DENIED);
            }

            database.getUserManager().dropUser(userName);
        }

        private void processDropSequence()
        {

            bool ifexists = false;

            session.checkAdmin();
            session.checkDDLWrite();

            string name = tokenizer.getName();
            string schemaname =
                session.getSchemaNameForWrite(tokenizer.getLongNameFirst());

            if (tokenizer.isGetThis(Token.T_IF))
            {
                tokenizer.getThis(Token.T_EXISTS);

                ifexists = true;
            }

            bool cascade = tokenizer.isGetThis(Token.T_CASCADE);

            if (!cascade)
            {
                tokenizer.isGetThis(Token.T_RESTRICT);
            }

            NumberSequence sequence = database.schemaManager.findSequence(name,
                schemaname);

            if (sequence == null)
            {
                if (ifexists)
                {
                    return;
                }
                else
                {
                    throw Trace.error(Trace.SEQUENCE_NOT_FOUND);
                }
            }

            database.schemaManager.checkCascadeDropViews(sequence, cascade);
            database.schemaManager.dropSequence(sequence);
        }

        private void processDropTrigger()
        {

            session.checkAdmin();
            session.checkDDLWrite();

            string triggername = tokenizer.getName();
            string schemaname =
                session.getSchemaNameForWrite(tokenizer.getLongNameFirst());

            database.schemaManager.dropTrigger(session, triggername, schemaname);
        }

        private void processDropIndex()
        {

            string name = tokenizer.getName();
            string schema =
                session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
            bool ifexists = false;

            // accept a table name - no check performed if it is the right table
            if (tokenizer.isGetThis(Token.T_ON))
            {
                tokenizer.getName();
            }

            if (tokenizer.isGetThis(Token.T_IF))
            {
                tokenizer.getThis(Token.T_EXISTS);

                ifexists = true;
            }

            session.checkAdmin();
            session.checkDDLWrite();
            database.schemaManager.dropIndex(session, name, schema, ifexists);
        }

        private void processDropSchema()
        {

            string name = tokenizer.getSimpleName();
            bool cascade = tokenizer.isGetThis(Token.T_CASCADE);

            if (!cascade)
            {
                tokenizer.isGetThis(Token.T_RESTRICT);
            }

            processDropSchema(name, cascade);
        }

        private void processDropSchema(string name,
                                       bool cascade)
        {

            if (!database.schemaManager.schemaExists(name))
            {
                throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
            }

            database.schemaManager.dropSchema(name, cascade);

            if (name.Equals(session.getSchemaName(null)))
            {
                session.setSchema(database.schemaManager.getDefaultSchemaName());
            }
        }

        private Result processExplainPlan()
        {

            // PRE:  we assume only one DML or DQL has been submitted
            //       and simply ignore anything following the first
            //       sucessfully compliled statement
            string token;
            Parser parser;
            int cmd;
            CompiledStatement cs = null;
            Result result;
            string line;
            StringReader lnr;

            tokenizer.getThis(Token.T_PLAN);
            tokenizer.getThis(Token.T_FOR);

            parser = new Parser(session, database, tokenizer);
            token = tokenizer.getSimpleToken();
            cmd = Token.get(token);
            result = Result.newSingleColumnResult("OPERATION", Types.VARCHAR);

            int brackets = 0;

            switch (cmd)
            {

                case Token.OPENBRACKET:
                    brackets = parser.parseOpenBracketsSelect() + 1;
                    break;
                case Token.SELECT:
                    cs = parser.compileSelectStatement(brackets);
                    break;

                case Token.INSERT:
                    cs = parser.compileInsertStatement();
                    break;

                case Token.UPDATE:
                    cs = parser.compileUpdateStatement();
                    break;

                case Token.DELETE:
                    cs = parser.compileDeleteStatement();
                    break;

                case Token.CALL:
                    cs = parser.compileCallStatement();
                    break;

                default:

                    // - No real need to throw, so why bother?
                    // - Just return result with no rows for now
                    // - Later, maybe there will be plan desciptions
                    //   for other operations
                    return result;
            }

            lnr = new StringReader(cs.describe(session));

            while (null != (line = lnr.ReadLine()))
            {
                result.add(new Object[] { line });
            }

            return result;
        }

        // fredt@users 20010701 - patch 1.6.1 by fredt - open <1.60 db files
        // convert org.hsql.Library aliases from versions < 1.60 to org.hsqldb
        // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) - ABS function
        const string oldLib = "EffiProzDB.Library.";
        static readonly int oldLibLen = oldLib.Length;
        const string newLib = "EffiPrzDB.Library.";

        private static string upgradeMethodFQN(string fqn)
        {

            if (fqn.StartsWith(oldLib))
            {
                fqn = newLib + fqn.Substring(oldLibLen);
            }
            else if (fqn.Equals("System.Math.Abs"))
            {
                fqn = "EffiProzDB.Library.Abs";
            }

            return fqn;
        }

        /**
         * Processes a SELECT INTO for a new table.
         */
        public Result processSelectInto(Result result, HsqlNameManager.HsqlName intoHsqlName,
                                 int intoType)
        {

            // fredt@users 20020215 - patch 497872 by Nitin Chauhan
            // to require column labels _in SELECT INTO TABLE
            int colCount = result.getColumnCount();

            for (int i = 0; i < colCount; i++)
            {
                if (result.metaData.colLabels[i].Length == 0)
                {
                    throw Trace.error(Trace.LABEL_REQUIRED);
                }
            }

            // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
            Table t = (intoType == Table.TEXT_TABLE)
                      ? new TextTable(database, intoHsqlName, intoType)
                      : new Table(database, intoHsqlName, intoType);

            t.addColumns(result.metaData, result.getColumnCount());
            t.createPrimaryKey();
            database.schemaManager.linkTable(t);

            // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
            if (intoType == Table.TEXT_TABLE)
            {
                try
                {

                    // Use default lowercase name "<table>.csv" (with invalid
                    // char's converted to underscores):
                    string txtSrc =
                        StringUtil.toLowerSubset(intoHsqlName.name, '_') + ".csv";

                    t.setDataSource(session, txtSrc, false, true);
                    logTableDDL(t);
                    t.insertIntoTable(session, result);
                }
                catch (HsqlException e)
                {
                    database.schemaManager.dropTable(session, intoHsqlName.name,
                                                     null, false, false, false);

                    throw (e);
                }
            }
            else
            {
                logTableDDL(t);

                // SELECT .. INTO can't fail because of constraint violation
                t.insertIntoTable(session, result);
            }

            Result uc = new Result(ResultConstants.UPDATECOUNT);

            uc.updateCount = result.getSize();

            return uc;
        }

        /**
         *  Logs the DDL for a table created with INTO.
         *  Uses two dummy arguments for getTableDDL() as the new table has no
         *  FK constraints.
         *
         *
         * @param t table
         * @throws  HsqlException
         */
        private void logTableDDL(Table t)
        {

            StringBuilder tableDDL;
            string sourceDDL;

            tableDDL = new StringBuilder();

            DatabaseScript.getTableDDL(database, t, 0, null, true, tableDDL);

            sourceDDL = DatabaseScript.getDataSource(t);

            database.logger.writeToLog(session, tableDDL.ToString());

            if (sourceDDL != null)
            {
                database.logger.writeToLog(session, sourceDDL);
            }
        }

        private void processAlterTableAddUniqueConstraint(Table t,
                HsqlNameManager.HsqlName n)
        {

            int[] col;

            col = processColumnList(t, false);

            if (n == null)
            {
                n = database.nameManager.newAutoName("CT");
            }

            session.commit();

            TableWorks tableWorks = new TableWorks(session, t);

            tableWorks.createUniqueConstraint(col, n);
        }

        private void processAlterTableAddForeignKeyConstraint(Table t,
                HsqlNameManager.HsqlName n)
        {

            Constraint tc;

            if (n == null)
            {
                n = database.nameManager.newAutoName("FK");
            }

            tc = processCreateFK(t, n);

            checkFKColumnDefaults(t, tc);
            t.checkColumnsMatch(tc.core.mainColArray, tc.core.refTable,
                                tc.core.refColArray);
            session.commit();

            TableWorks tableWorks = new TableWorks(session, t);

            tableWorks.createForeignKey(tc.core.mainColArray, tc.core.refColArray,
                                        tc.constName, tc.core.refTable,
                                        tc.core.deleteAction,
                                        tc.core.updateAction);
        }

        private void processAlterTableAddCheckConstraint(Table table,
                HsqlNameManager.HsqlName name)
        {

            Constraint check;

            if (name == null)
            {
                name = database.nameManager.newAutoName("CT");
            }

            check = new Constraint(name, null, null, null, Constraint.CHECK,
                                   Constraint.NO_ACTION, Constraint.NO_ACTION);

            processCreateCheckConstraintCondition(check);
            session.commit();

            TableWorks tableWorks = new TableWorks(session, table);

            tableWorks.createCheckConstraint(check, name);
        }

        private void processAlterTableAddPrimaryKey(Table t,
                HsqlNameManager.HsqlName n)
        {

            int[] col;

            col = processColumnList(t, false);

            session.commit();

            TableWorks tableWorks = new TableWorks(session, t);

            tableWorks.addPrimaryKey(col, n);
        }

        private void processReleaseSavepoint()
        {

            string token;

            tokenizer.getThis(Token.T_SAVEPOINT);

            token = tokenizer.getSimpleName();

            session.releaseSavepoint(token);
        }

        private void processAlterUser()
        {

            string userName;
            string password;
            User userObject;

            userName = getUserIdentifier();
            userObject = (User)database.getUserManager().getUsers()[userName];

            Trace.check(userObject != null, Trace.USER_NOT_FOUND, userName);
            tokenizer.getThis(Token.T_SET);
            tokenizer.getThis(Token.T_PASSWORD);

            password = getPassword();

            userObject.setPassword(password);
            database.logger.writeToLog(session, userObject.getAlterUserDDL());
            session.setScripting(false);
        }

        private string getUserIdentifier()
        {

            string token = tokenizer.getString();
            Tokenizer t = new Tokenizer(token);

            return t.getSimpleName();
        }

        private string getPassword()
        {

            string token = tokenizer.getString();

            return token.ToUpper(System.Globalization.CultureInfo.CreateSpecificCulture("EN-us"));
        }

        /**
         *  Responsible for handling the execution of GRANT/REVOKE role...
         *  statements.
         *
         * @ 
         */
        private void processRoleGrantOrRevoke(bool grant)
        {

            string token;
            List<string> list = new List<string>();
            string role;
            GranteeManager granteeManager = database.getGranteeManager();

            do
            {
                role = tokenizer.getSimpleToken();

                Trace.check(granteeManager.isRole(role),
                            (grant ? Trace.NO_SUCH_ROLE_GRANT
                                   : Trace.NO_SUCH_ROLE_REVOKE));
                list.Add(role);
            } while (tokenizer.isGetThis(Token.T_COMMA));

            tokenizer.getThis(grant ? Token.T_TO
                                    : Token.T_FROM);

            token = getUserIdentifier();

            GranteeManager gm = database.getGranteeManager();

            for (int i = 0; i < list.Count; i++)
            {
                if (grant)
                {
                    gm.grant(token, (String)list[i]);
                }
                else
                {
                    gm.revoke(token, (String)list[i]);
                }
            }
        }
    }
}